package com.javiercp.ayudaapp;

import com.javiercp.ayudaapp.location.LocationHelper;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

// Service to get the best location and send it to an activity. 
// Sample by raulhc.

public class LocationService extends Service {
	
	private Handler mActivityHandler = null; 

	private final IBinder mBinder = new LocationBinder();
	
	@Override
	public IBinder onBind(Intent arg0) {
		return mBinder;
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		return Service.START_STICKY;
	}

	private void sendMessageToActivity(int what, String key, String value) {
		if (mActivityHandler == null) return;
		
		Message msg = mActivityHandler.obtainMessage(what);
		Bundle bundle = new Bundle();
		bundle.putString(key, value);
		msg.setData(bundle);
		mActivityHandler.sendMessage(msg);
	}
	
	public class LocationBinder extends Binder implements LocationListener {
		
		private static final int TWO_MINUTES = 1000 * 60 * 2;
		
		private boolean mmIsRunning = false;
		
		private String mcurrProvider;
		private LocationManager mlocationManager;
		private Location mlocation;
		
		public boolean isRunning() {
			return mmIsRunning;
		}
		
		public void registerHandler(Handler handler) {
			mActivityHandler = handler;
		}
		
		public void init() {
	        Log.i(MyConstants.LOGTAG, "Registering location services");
	        
	        mmIsRunning = true;
	        mlocationManager = (LocationManager)getApplicationContext().getSystemService(Context.LOCATION_SERVICE);    
	    	mlocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
	    	mcurrProvider = LocationManager.GPS_PROVIDER;
		}
		
		public void stop() {
			Log.i(MyConstants.LOGTAG, "Unregistering location services");
			
			mmIsRunning = false;
			mlocationManager.removeUpdates(this);
		}
	
		// This method will get the current best location or the last known one if the isn't a GPS fix yet
		public Location getLocationFast() {
			Log.i(MyConstants.LOGTAG, "Getting fast Location");
			if ((mlocation == null) && (mlocationManager != null)) {
				return mlocationManager.getLastKnownLocation(mcurrProvider);		
			}
			else {
				return mlocation;
			}
		}
		
		//------------------ Location Events Methods ------------------
		@Override
		public void onLocationChanged(Location location) {
			Log.i(MyConstants.LOGTAG, "onLocationChanged");
			
			if (isBetterLocation(location, mlocation)) {
				mlocation = location;
				
				sendMessageToActivity(MyConstants.GOT_LOCATION, MyConstants.LOCATION_KEY, LocationHelper.serialize(location));
			}
		}

		// We are going to try to use the best locatino provider. 
		// First GPS_PROVIDER if available, and if it is disabled we'll try NETWORK_PROVIDER
		@Override
		public void onProviderDisabled(String provider) {
			Log.i(MyConstants.LOGTAG, "onProviderDisabled");
			
			if (isSameProvider(provider, LocationManager.GPS_PROVIDER)) {
				Log.w(MyConstants.LOGTAG, "GPS disabled, changing to network");
				
				mlocationManager.removeUpdates(this);
				mlocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
				mcurrProvider = LocationManager.NETWORK_PROVIDER;
			}
			else {
				Log.w(MyConstants.LOGTAG, "GPS and network disabled");
				mlocationManager.removeUpdates(this);
				mcurrProvider = "_NONE_";
			}
		}

		// We are going to try to use the best location provider. 
		// If GPS_PROVIDER is enabled, we switch to it. 
		// If NETWORK_PROVIDER is enabled, we only switch if we are not using GPS_PROVIDER
		@Override
		public void onProviderEnabled(String provider) {
			Log.i(MyConstants.LOGTAG, "onProviderEnabled");
			
			if (isSameProvider(provider, LocationManager.GPS_PROVIDER)) {
				mlocationManager.removeUpdates(this);
				mlocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
				mcurrProvider = LocationManager.GPS_PROVIDER;
			}
			else {
				if (!isSameProvider(mcurrProvider, LocationManager.GPS_PROVIDER)) {
					mlocationManager.removeUpdates(this);
					mlocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
					mcurrProvider = LocationManager.NETWORK_PROVIDER;
				}
			}
		}

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
			Log.i(MyConstants.LOGTAG, "onStatusChanged");
		}
		
		//--- sample code by Google
		protected boolean isBetterLocation(Location location, Location currentBestLocation) {
		    if (currentBestLocation == null) {
		        // A new location is always better than no location
		        return true;
		    }

		    // Check whether the new location fix is newer or older
		    long timeDelta = location.getTime() - currentBestLocation.getTime();
		    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
		    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
		    boolean isNewer = timeDelta > 0;

		    // If it's been more than two minutes since the current location, use the new location
		    // because the user has likely moved
		    if (isSignificantlyNewer) {
		        return true;
		    // If the new location is more than two minutes older, it must be worse
		    } else if (isSignificantlyOlder) {
		        return false;
		    }

		    // Check whether the new location fix is more or less accurate
		    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
		    boolean isLessAccurate = accuracyDelta > 0;
		    boolean isMoreAccurate = accuracyDelta < 0;
		    boolean isSignificantlyLessAccurate = accuracyDelta > 200;

		    // Check if the old and new location are from the same provider
		    boolean isFromSameProvider = isSameProvider(location.getProvider(),
		            currentBestLocation.getProvider());

		    // Determine location quality using a combination of timeliness and accuracy
		    if (isMoreAccurate) {
		        return true;
		    } else if (isNewer && !isLessAccurate) {
		        return true;
		    } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
		        return true;
		    }
		    return false;
		}

		private boolean isSameProvider(String provider1, String provider2) {
		    if (provider1 == null) {
		      return provider2 == null;
		    }
		    return provider1.equals(provider2);
		}
	}
}

